package com.aiwiown.snackmq.storage.file;

import com.aiwiown.snackmq.storage.config.StorageConfig;
import com.aiwiown.snackmq.storage.util.ResourceManager;
import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * 映射文件队列，用于管理多个映射文件
 */
@Slf4j
public class MappedFileQueue {
    private final String storePath;
    private final StorageConfig config;
    // 使用线程安全的集合
    private final CopyOnWriteArrayList<MappedFile> mappedFiles;
    private final AtomicLong totalSize;
    private final AtomicLong currentFileIndex;
    // 使用读写锁提高并发性能
    private volatile MappedFile currentMappedFile;
    private final ReadWriteLock writeLock = new ReentrantReadWriteLock();
    private final long mappedFileSize;

    /**
     * 构造函数
     *
     * @param config 存储配置
     */
    public MappedFileQueue(StorageConfig config) {
        this.storePath = config.getMessageStorePath();
        this.config = config;
        this.mappedFiles = new CopyOnWriteArrayList<>();
        this.totalSize = new AtomicLong(0);
        this.currentFileIndex = new AtomicLong(0);
        this.mappedFileSize = config.getMappedFileSize();
    }

    /**
     * 初始化
     */
    public void init() throws IOException {
        // 确保目录存在
        File dir = new File(storePath);
        if (!dir.exists()) {
            dir.mkdirs();
        }

        // 加载已存在的文件
        File[] files = dir.listFiles();
        if (files != null) {
            for (File file : files) {
                if (file.isFile() && file.getName().endsWith(".msg")) {
                    try {
                        MappedFile mappedFile = new MappedFile(file.getName(), config);
                        mappedFiles.add(mappedFile);
                        totalSize.addAndGet(mappedFile.getWrotePosition());
                        currentFileIndex.incrementAndGet();
                    } catch (IOException e) {
                        log.error("Failed to load mapped file: {}", file.getPath(), e);
                    }
                }
            }
        }

        // 如果没有文件，创建第一个文件
        if (mappedFiles.isEmpty()) {
            createNewMappedFile();
        } else {
            // 使用最新的文件作为当前文件
            currentMappedFile = mappedFiles.get(mappedFiles.size() - 1);
        }
    }

    /**
     * 创建新的映射文件
     */
    private void createNewMappedFile() throws IOException {
        String fileName = String.format("%020d.msg", currentFileIndex.get());
        MappedFile mappedFile = new MappedFile(fileName, config);
        mappedFiles.add(mappedFile);
        currentMappedFile = mappedFile;
        currentFileIndex.incrementAndGet();
    }

    /**
     * 提交消息
     */
    public void commit() {
        if (currentMappedFile != null) {
            currentMappedFile.commit();
        }
    }

    /**
     * 刷盘
     */
    public void flush() {
        for (MappedFile mappedFile : mappedFiles) {
            mappedFile.flush();
        }
    }

    /**
     * 异步刷盘
     */
    public void flushAsync() {
        for (MappedFile mappedFile : mappedFiles) {
            mappedFile.flushAsync();
        }
    }

    /**
     * 读取消息
     *
     * @param offset 消息偏移量
     * @param length 消息长度
     * @return 消息内容
     */
    public byte[] readMessage(long offset, int length) throws IOException {
        if (offset < 0 || length <= 0) {
            log.error("Invalid offset or length: offset={}, length={}", offset, length);
            return null;
        }
        // 计算文件索引和文件内偏移量
        int fileIndex = (int) (offset / mappedFileSize);
        long fileOffset = offset % mappedFileSize;

        // 检查文件索引是否有效
        if (fileIndex >= mappedFiles.size()) {
            log.error("Invalid file index: index={}, total files={}", fileIndex, mappedFiles.size());
            return null;
        }

        // 获取对应的MappedFile
        MappedFile mappedFile = mappedFiles.get(fileIndex);
        if (mappedFile == null) {
            log.error("MappedFile not found: index={}", fileIndex);
            return null;
        }

        // 检查消息是否会跨越文件边界
        if (fileOffset + length > mappedFileSize) {
            log.error("Message crosses file boundary: offset={}, length={}, fileSize={}, fileIndex={}, fileOffset={}",
                    offset, length, mappedFileSize, fileIndex, fileOffset);
            return null;
        }

        // 读取消息
        byte[] message = mappedFile.readMessage(fileOffset, length);
        if (message == null) {
            log.error("Failed to read message at offset={}, length={}, fileIndex={}, fileOffset={}",
                    offset, length, fileIndex, fileOffset);
        }
        return message;
    }

    /**
     * 获取总大小
     *
     * @return 总大小
     */
    public long getTotalSize() {
        return totalSize.get();
    }

    /**
     * 获取文件数量
     *
     * @return 文件数量
     */
    public int getMappedFileCount() {
        return mappedFiles.size();
    }

    /**
     * 获取所有文件
     *
     * @return 文件列表
     */
    public List<MappedFile> getMappedFiles() {
        writeLock.readLock().lock();
        try {
            return new ArrayList<>(mappedFiles);
        } finally {
            writeLock.readLock().unlock();
        }
    }

    /**
     * 关闭并清理资源
     */
    public void close() {
        synchronized (this) {
            // 关闭所有映射文件
            for (MappedFile mappedFile : mappedFiles) {
                if (mappedFile != null) {
                    mappedFile.close();
                }
            }
            mappedFiles.clear();
            currentMappedFile = null;
            totalSize.set(0);
            log.info("Closed mapped file queue: {}", new File(storePath).getAbsolutePath());
        }
    }

    /**
     * 删除所有文件
     */
    public void delete() {
        synchronized (this) {
            // 删除所有映射文件
            for (MappedFile mappedFile : mappedFiles) {
                if (mappedFile != null) {
                    mappedFile.delete();
                }
            }
            mappedFiles.clear();
            currentMappedFile = null;
            totalSize.set(0);

            // 尝试删除目录
            File dir = new File(storePath);
            if (dir.exists()) {
                if (deleteDirectory(dir)) {
                    log.info("Deleted directory: {}", dir.getAbsolutePath());
                } else {
                    log.warn("Failed to delete directory: {}", dir.getAbsolutePath());
                }
            }
        }
    }

    /**
     * 递归删除目录
     *
     * @param dir 目录
     * @return 删除结果
     */
    private boolean deleteDirectory(File dir) {
        File[] files = dir.listFiles();
        if (files != null) {
            for (File file : files) {
                if (file.isDirectory()) {
                    deleteDirectory(file);
                } else {
                    if (!ResourceManager.safeDeleteFile(file)) {
                        log.warn("Failed to delete file: {}", file.getAbsolutePath());
                    }
                }
            }
        }
        return dir.delete();
    }

    /**
     * 删除过期文件
     *
     * @param expireTime 过期时间
     */
    public void deleteExpiredFiles(long expireTime) {
        if (!config.isRetainFiles()) {
            return;
        }

        for (MappedFile mappedFile : mappedFiles) {
            File file = new File(storePath, mappedFile.getFileName());
            if (file.lastModified() < expireTime) {
                mappedFile.close();
                file.delete();
                mappedFiles.remove(mappedFile);
                totalSize.addAndGet(-mappedFile.getFileSize());
            }
        }
    }

    /**
     * 获取当前写入位置
     *
     * @return 当前写入位置
     */
    public long getWrotePosition() {
        return totalSize.get();
    }

    /**
     * 写入消息
     *
     * @param message 消息内容（已包含长度前缀）
     * @return 消息的偏移量，如果写入失败则返回-1
     */
    public long writeMessage(byte[] message) {
        if (message == null || message.length == 0) {
            log.error("Invalid message: null or empty");
            return -1;
        }
        // 验证消息长度（消息已经包含长度前缀）
        if (message.length > mappedFileSize - 1024) {
            log.error("Message too large: length={}, max={}", message.length, mappedFileSize - 1024);
            return -1;
        }
        writeLock.writeLock().lock();
        try {
            // 检查当前文件是否有足够空间
            if (currentMappedFile == null || !currentMappedFile.hasSpace(message.length)) {
                // 创建新文件
                if (!createMappedFile()) {
                    log.error("Failed to create new mapped file");
                    return -1;
                }
            }
            // 计算消息的偏移量
            long offset = calculateOffset(currentMappedFile);
            // 写入消息（直接写入，不添加长度前缀）
            if (!currentMappedFile.writeMessageDirect(message)) {
                log.error("Failed to write message to mapped file");
                return -1;
            }

            // 更新总大小
            totalSize.addAndGet(message.length);
            return offset;
        } finally {
            writeLock.writeLock().unlock();
        }
    }

    /**
     * 计算消息的偏移量
     *
     * @param mappedFile 当前映射文件
     * @return 消息的偏移量
     */
    private long calculateOffset(MappedFile mappedFile) {
        writeLock.readLock().lock();
        try {
            int fileIndex = mappedFiles.indexOf(mappedFile);
            if (fileIndex == -1) {
                log.error("MappedFile not found in file list");
                return -1;
            }
            return (long) fileIndex * mappedFileSize + mappedFile.getWrotePosition();
        } finally {
            writeLock.readLock().unlock();
        }
    }

    /**
     * 获取最后一个映射文件
     *
     * @return 最后一个映射文件
     */
    private MappedFile getLastMappedFile() {
        writeLock.readLock().lock();
        try {
            if (mappedFiles.isEmpty()) {
                try {
                    createNewMappedFile();
                } catch (IOException e) {
                    log.error("Failed to create new mapped file", e);
                    return null;
                }
            }
            return mappedFiles.get(mappedFiles.size() - 1);
        } finally {
            writeLock.readLock().unlock();
        }
    }

    /**
     * 回滚写入位置
     */
    public void rollback(long length) {
        writeLock.writeLock().lock();
        try {
            // 获取当前映射文件
            MappedFile mappedFile = getLastMappedFile();
            if (mappedFile == null) {
                log.error("No mapped file available for rollback");
                return;
            }

            // 回滚写入位置
            mappedFile.rollback(length);
            totalSize.addAndGet(-length);
        } catch (Exception e) {
            log.error("Failed to rollback write position", e);
        } finally {
            writeLock.writeLock().unlock();
        }
    }

    /**
     * 获取映射文件
     */
    private MappedFile getMappedFile(int index) {
        if (index < 0 || index >= mappedFiles.size()) {
            return null;
        }
        return mappedFiles.get(index);
    }
    
    /**
     * 根据文件索引获取映射文件
     * 
     * @param fileIndex 文件索引
     * @return 映射文件，如果不存在则返回null
     */
    public MappedFile getMappedFileByIndex(int fileIndex) {
        writeLock.readLock().lock();
        try {
            if (fileIndex < 0 || fileIndex >= mappedFiles.size()) {
                log.debug("Invalid file index: {}, total files: {}", fileIndex, mappedFiles.size());
                return null;
            }
            return mappedFiles.get(fileIndex);
        } finally {
            writeLock.readLock().unlock();
        }
    }

    /**
     * 创建新的映射文件
     */
    private boolean createMappedFile() {
        String mappedFileName = String.format("%020d.msg", mappedFiles.size());
        try {
            // 创建映射文件
            MappedFile mappedFile = new MappedFile(mappedFileName, config);
            mappedFiles.add(mappedFile);
            currentMappedFile = mappedFile;
            log.info("Created new mapped file: {}", mappedFile.getFileName());
            return true;
        } catch (Exception e) {
            log.error("Failed to create mapped file: {}", mappedFileName, e);
            return false;
        }
    }

    /**
     * 保存写入位置
     */
    public void saveWritePosition() {
        if (currentMappedFile != null) {
            try {
                // 在文件末尾写入写入位置
                currentMappedFile.saveWritePosition();
            } catch (Exception e) {
                log.error("Failed to save write position", e);
            }
        }
    }

    /**
     * 恢复写入位置
     */
    public void restoreWritePosition() {
        if (currentMappedFile != null) {
            try {
                // 从文件末尾读取写入位置
                currentMappedFile.restoreWritePosition();
            } catch (Exception e) {
                log.error("Failed to restore write position", e);
            }
        }
    }

    /**
     * 获取当前映射文件
     *
     * @return 当前映射文件
     */
    public MappedFile getCurrentMappedFile() {
        return currentMappedFile;
    }
}